home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / VoiceClientServer / VoiceServer / voiceserver.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  29.3 KB  |  789 lines

  1. //----------------------------------------------------------------------------
  2. // File: VoiceServer.cpp
  3. //
  4. // Desc: The VoiceClientServer sample is a simple DirectPlay 
  5. //       voice-based client/server application. 
  6. //
  7. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  8. //-----------------------------------------------------------------------------
  9. #define STRICT
  10. #include <windows.h>
  11. #include <basetsd.h>
  12. #include <dplay8.h>
  13. #include <dpaddr.h>
  14. #include <dxerr8.h>
  15. #include <tchar.h>
  16. #include "NetVoice.h"
  17. #include "VoiceClientServer.h"
  18. #include "DXUtil.h"
  19. #include "resource.h"
  20.  
  21.  
  22.  
  23.  
  24. //-----------------------------------------------------------------------------
  25. // Player context locking defines
  26. //-----------------------------------------------------------------------------
  27. CRITICAL_SECTION g_csPlayerContext;
  28. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  29. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  30. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
  31. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  32.  
  33.  
  34. //-----------------------------------------------------------------------------
  35. // Defines, and constants
  36. //-----------------------------------------------------------------------------
  37. #define VOICESERVER_DEFAULT_PORT           0x6502 // arbitrary port number for this app
  38.  
  39. struct APP_PLAYER_INFO
  40. {
  41.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  42.                                             // are done w/ this object
  43.     DPNID dpnidPlayer;                      // DPNID of player
  44.     TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
  45. };
  46.  
  47.  
  48.  
  49.  
  50. //-----------------------------------------------------------------------------
  51. // Global variables
  52. //-----------------------------------------------------------------------------
  53. IDirectPlay8Server* g_pDPServer                  = NULL;    // DirectPlay server object
  54. CNetVoice*         g_pNetVoice                   = NULL;    // DirectPlay voice helper class
  55. HINSTANCE          g_hInst                       = NULL;    // HINST of app
  56. HWND               g_hDlg                        = NULL;    // HWND of main dialog
  57. LONG               g_lNumberOfActivePlayers      = 0;       // Number of players currently in game
  58. TCHAR              g_strAppName[256]             = TEXT("VoiceServer");
  59. TCHAR              g_strSessionName[MAX_PATH];              // Session name
  60. DWORD              g_dwPort;                                // Port
  61. HRESULT            g_hrDialog;                              // Exit code for app 
  62. BOOL               g_bServerStarted              = FALSE;   // TRUE if the server has started
  63. GUID               g_guidDVSessionCT;                       // GUID for choosen voice compression
  64.  
  65.  
  66.  
  67.  
  68. //-----------------------------------------------------------------------------
  69. // Function-prototypes
  70. //-----------------------------------------------------------------------------
  71. HRESULT WINAPI   DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  72. HRESULT CALLBACK DirectPlayVoiceServerMessageHandler( LPVOID lpvUserContext, DWORD dwMessageType, LPVOID lpMessage );
  73. INT_PTR CALLBACK ServerDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  74. HRESULT  StartServer( HWND hDlg );
  75. VOID     StopServer( HWND hDlg );
  76. VOID     DisplayPlayers( HWND hDlg );
  77. HRESULT  SendCreatePlayerMsg( APP_PLAYER_INFO* pPlayerInfo, DPNID dpnidTarget );
  78. HRESULT  SendWorldStateToNewPlayer( DPNID dpnidPlayer );
  79. HRESULT  SendDestroyPlayerMsgToAll( APP_PLAYER_INFO* pPlayerInfo );
  80. HRESULT  EnumVoiceCompressionCodecs( HWND hDlg );
  81.  
  82.  
  83.  
  84.  
  85.  
  86. //-----------------------------------------------------------------------------
  87. // Name: WinMain()
  88. // Desc: Entry point for the application.  Since we use a simple dialog for 
  89. //       user interaction we don't need to pump messages.
  90. //-----------------------------------------------------------------------------
  91. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  92.                       LPSTR pCmdLine, INT nCmdShow )
  93. {
  94.     HKEY    hDPlaySampleRegKey;
  95.     BOOL    bConnectSuccess = FALSE;
  96.  
  97.     g_hInst = hInst; 
  98.     InitializeCriticalSection( &g_csPlayerContext );
  99.  
  100.     // Read persistent state information from registry
  101.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  102.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  103.                     &hDPlaySampleRegKey, NULL );
  104.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
  105.                              g_strSessionName, MAX_PATH, TEXT("TestGame") );
  106.     DXUtil_ReadIntRegKey( hDPlaySampleRegKey, TEXT("VoiceServer Port"), 
  107.                           &g_dwPort, VOICESERVER_DEFAULT_PORT );
  108.  
  109.     // Init COM so we can use CoCreateInstance
  110.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  111.  
  112.     // For this sample, we just start a simple dialog box server
  113.     g_hrDialog = S_OK;
  114.     DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN), NULL, (DLGPROC) ServerDlgProc );
  115.  
  116.     if( FAILED( g_hrDialog ) )
  117.     {
  118.         if( g_hrDialog == DPNERR_CONNECTIONLOST )
  119.         {
  120.             MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  121.                         TEXT("The server will now quit."),
  122.                         g_strAppName, MB_OK | MB_ICONERROR );
  123.         }
  124.         else
  125.         {
  126.             DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  127.             MessageBox( NULL, TEXT("An error occured. ")
  128.                         TEXT("The server will now quit."),
  129.                         g_strAppName, MB_OK | MB_ICONERROR );
  130.         }
  131.     }
  132.  
  133.     DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
  134.     DXUtil_WriteIntRegKey( hDPlaySampleRegKey, TEXT("VoiceServer Port"), g_dwPort );
  135.  
  136.     StopServer( NULL );
  137.  
  138.     RegCloseKey( hDPlaySampleRegKey );
  139.     DeleteCriticalSection( &g_csPlayerContext );
  140.     CoUninitialize();
  141.  
  142.     return TRUE;
  143. }
  144.  
  145.  
  146.  
  147.  
  148. //-----------------------------------------------------------------------------
  149. // Name: ServerDlgProc()
  150. // Desc: Handles dialog messages
  151. //-----------------------------------------------------------------------------
  152. INT_PTR CALLBACK ServerDlgProc( HWND hDlg, UINT msg, 
  153.                                 WPARAM wParam, LPARAM lParam )
  154. {
  155.     switch( msg ) 
  156.     {
  157.         case WM_INITDIALOG:
  158.         {
  159.             g_hDlg = hDlg;
  160.  
  161.             // Load and set the icon
  162.             HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  163.             SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  164.             SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  165.  
  166.             SetWindowText( hDlg, TEXT("VoiceServer") );
  167.             SetDlgItemText( hDlg, IDC_SESSION_NAME, g_strSessionName );
  168.  
  169.             // Set the port to either a number or blank
  170.             if( g_dwPort != 0 )
  171.                 SetDlgItemInt( hDlg, IDC_PORT, g_dwPort, FALSE );
  172.             else
  173.                 SetDlgItemText( hDlg, IDC_PORT, TEXT("") );
  174.  
  175.             SetDlgItemText( hDlg, IDC_STATUS, TEXT("Server stoped.") );
  176.  
  177.             EnumVoiceCompressionCodecs( hDlg );
  178.             CheckRadioButton( hDlg, IDC_FORWARDING, IDC_MIXING, IDC_FORWARDING );
  179.  
  180.             PostMessage( hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  181.             break;
  182.         }
  183.  
  184.         case WM_APP_UPDATE_STATS:
  185.         {
  186.             // Update the number of players in the game
  187.             TCHAR strNumberPlayers[32];
  188.  
  189.             wsprintf( strNumberPlayers, TEXT("%d"), g_lNumberOfActivePlayers );
  190.             SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  191.             DisplayPlayers( hDlg );
  192.             break;
  193.         }
  194.  
  195.         case WM_COMMAND:
  196.         {
  197.             switch( LOWORD(wParam) )
  198.             {
  199.                 case IDC_START:
  200.                     if( !g_bServerStarted )
  201.                     {
  202.                         if( FAILED( g_hrDialog = StartServer( hDlg ) ) )
  203.                         {
  204.                             DXTRACE_ERR( TEXT("StartServer"), g_hrDialog );
  205.                             EndDialog( hDlg, 0 );
  206.                         }
  207.                     }
  208.                     else
  209.                     {
  210.                         StopServer( hDlg );
  211.                     }
  212.  
  213.                     // Update the UI
  214.                     if( g_bServerStarted )
  215.                         SetDlgItemText( hDlg, IDC_START, TEXT("Stop Server") );
  216.                     else
  217.                         SetDlgItemText( hDlg, IDC_START, TEXT("Start Server") );
  218.  
  219.                     EnableWindow( GetDlgItem( hDlg, IDC_SESSION_NAME ), !g_bServerStarted );
  220.                     EnableWindow( GetDlgItem( hDlg, IDC_COMPRESSION_COMBO ), !g_bServerStarted );
  221.                     EnableWindow( GetDlgItem( hDlg, IDC_FORWARDING ), !g_bServerStarted );
  222.                     EnableWindow( GetDlgItem( hDlg, IDC_MIXING ), !g_bServerStarted );                                               
  223.                     EnableWindow( GetDlgItem( hDlg, IDC_PORT ), !g_bServerStarted );
  224.  
  225.                     break;
  226.  
  227.                 case IDCANCEL:
  228.                     StopServer( hDlg );
  229.                     EndDialog( hDlg, 0 );
  230.                     return TRUE;
  231.             }
  232.             break;
  233.         }
  234.     }
  235.  
  236.     return FALSE; // Didn't handle message
  237. }
  238.  
  239.  
  240.  
  241.  
  242. //-----------------------------------------------------------------------------
  243. // Name: EnumVoiceCompressionCodecs()
  244. // Desc: Asks DirectPlay Voice what voice compression codecs are availible
  245. //       and fills the combo box thier names and GUIDs.
  246. //-----------------------------------------------------------------------------
  247. HRESULT EnumVoiceCompressionCodecs( HWND hDlg )
  248. {
  249.     LPDIRECTPLAYVOICECLIENT pVoiceClient        = NULL;
  250.     LPDVCOMPRESSIONINFO     pdvCompressionInfo  = NULL;
  251.     LPGUID  pGuid         = NULL;
  252.     LPBYTE  pBuffer       = NULL;
  253.     DWORD   dwSize        = 0;
  254.     DWORD   dwNumElements = 0;
  255.     HWND    hPulldown     = GetDlgItem( hDlg, IDC_COMPRESSION_COMBO );
  256.     HRESULT hr;
  257.     LONG    lIndex;
  258.     LONG    lFirst;
  259.  
  260.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  261.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlayVoiceClient, NULL, 
  262.                                        CLSCTX_INPROC_SERVER, IID_IDirectPlayVoiceClient, 
  263.                                        (VOID**) &pVoiceClient ) ) )
  264.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  265.  
  266.     hr = pVoiceClient->GetCompressionTypes( pBuffer, &dwSize, &dwNumElements, 0 );
  267.     if( hr != DVERR_BUFFERTOOSMALL && FAILED(hr) )
  268.         return DXTRACE_ERR( TEXT("GetCompressionTypes"), hr );
  269.  
  270.     pBuffer = new BYTE[dwSize];
  271.     if( FAILED( hr = pVoiceClient->GetCompressionTypes( pBuffer, &dwSize, 
  272.                                                         &dwNumElements, 0 ) ) )
  273.         return DXTRACE_ERR( TEXT("GetCompressionTypes"), hr );
  274.  
  275.     SAFE_RELEASE( pVoiceClient );
  276.     CoUninitialize();
  277.  
  278.     pdvCompressionInfo = (LPDVCOMPRESSIONINFO) pBuffer;
  279.     for( DWORD dwIndex = 0; dwIndex < dwNumElements; dwIndex++ )
  280.     {
  281.         TCHAR strName[MAX_PATH];
  282.         DXUtil_ConvertWideStringToGeneric( strName, 
  283.                                            pdvCompressionInfo[dwIndex].lpszName );
  284.  
  285.         lIndex = (LONG)SendMessage( hPulldown, CB_ADDSTRING, 0, (LPARAM) strName );
  286.  
  287.         pGuid = new GUID;
  288.         (*pGuid) = pdvCompressionInfo[dwIndex].guidType;
  289.         SendMessage( hPulldown, CB_SETITEMDATA, lIndex, (LPARAM) pGuid );
  290.  
  291.         if( pdvCompressionInfo[dwIndex].guidType == DPVCTGUID_SC03 )
  292.             lFirst = lIndex;
  293.     }
  294.  
  295.     SAFE_DELETE_ARRAY( pBuffer );
  296.     SendMessage( hPulldown, CB_SETCURSEL, lFirst, 0 );
  297.  
  298.     return S_OK;
  299. }
  300.  
  301.  
  302.  
  303.  
  304. //-----------------------------------------------------------------------------
  305. // Name: DirectPlayMessageHandler
  306. // Desc: Handler for DirectPlay messages.  This function is called by
  307. //       the DirectPlay message handler pool of threads, so be careful of thread
  308. //       synchronization problems with shared memory
  309. //-----------------------------------------------------------------------------
  310. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  311.                                          DWORD dwMessageId, 
  312.                                          PVOID pMsgBuffer )
  313. {
  314.     // Try not to stay in this message handler for too long, otherwise
  315.     // there will be a backlog of data.  The best solution is to 
  316.     // queue data as it comes in, and then handle it on other threads.
  317.     
  318.     // This function is called by the DirectPlay message handler pool of 
  319.     // threads, so be careful of thread synchronization problems with shared memory
  320.  
  321.     switch( dwMessageId )
  322.     {
  323.         case DPN_MSGID_CREATE_PLAYER:
  324.         {
  325.             HRESULT hr;
  326.             PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  327.             pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;
  328.  
  329.             // Get the peer info and extract its name
  330.             DWORD dwSize = 0;
  331.             DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  332.             hr = g_pDPServer->GetClientInfo( pCreatePlayerMsg->dpnidPlayer, 
  333.                                              pdpPlayerInfo, &dwSize, 0 );
  334.             if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
  335.             {
  336.                 if( hr == DPNERR_INVALIDPLAYER )
  337.                 {
  338.                     // Ignore this message if this is for the host
  339.                     break;
  340.                 }
  341.  
  342.                 return DXTRACE_ERR( TEXT("GetClientInfo"), hr );
  343.             }
  344.             pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  345.             ZeroMemory( pdpPlayerInfo, dwSize );
  346.             pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  347.             hr = g_pDPServer->GetClientInfo( pCreatePlayerMsg->dpnidPlayer, 
  348.                                        pdpPlayerInfo, &dwSize, 0 );
  349.             if( FAILED(hr) )
  350.                 return DXTRACE_ERR( TEXT("GetClientInfo"), hr );
  351.  
  352.             // Create a new and fill in a APP_PLAYER_INFO
  353.             APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  354.             ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  355.             pPlayerInfo->lRefCount   = 1;
  356.             pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  357.  
  358.             // This stores a extra TCHAR copy of the player name for 
  359.             // easier access.  This will be redundent copy since DPlay 
  360.             // also keeps a copy of the player name in GetClientInfo()
  361.             DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName, 
  362.                                                pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );
  363.  
  364.             SAFE_DELETE_ARRAY( pdpPlayerInfo );
  365.  
  366.             // Tell DirectPlay to store this pPlayerInfo 
  367.             // pointer in the pvPlayerContext.
  368.             pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  369.  
  370.             // Send all connected players a message telling about this new player
  371.             SendCreatePlayerMsg( pPlayerInfo, DPNID_ALL_PLAYERS_GROUP );
  372.  
  373.             // Tell this new player about the world state
  374.             SendWorldStateToNewPlayer( pCreatePlayerMsg->dpnidPlayer );
  375.  
  376.             // Update the number of active players, and 
  377.             // post a message to the dialog thread to update the 
  378.             // UI.  This keeps the DirectPlay message handler 
  379.             // from blocking
  380.             InterlockedIncrement( &g_lNumberOfActivePlayers );
  381.             if( g_hDlg != NULL )
  382.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  383.  
  384.             break;
  385.         }
  386.  
  387.         case DPN_MSGID_DESTROY_PLAYER:
  388.         {
  389.             PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  390.             pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  391.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  392.  
  393.             // Ignore this message if this is the host player
  394.             if( pPlayerInfo == NULL )
  395.                 break; 
  396.  
  397.             // Send all connected players a message telling about this destroyed player
  398.             SendDestroyPlayerMsgToAll( pPlayerInfo );
  399.  
  400.             PLAYER_LOCK();                  // enter player context CS
  401.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  402.             PLAYER_UNLOCK();                // leave player context CS
  403.  
  404.             // Update the number of active players, and 
  405.             // post a message to the dialog thread to update the 
  406.             // UI.  This keeps the DirectPlay message handler 
  407.             // from blocking
  408.             InterlockedDecrement( &g_lNumberOfActivePlayers );
  409.             if( g_hDlg != NULL )
  410.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  411.  
  412.             break;
  413.         }
  414.  
  415.         case DPN_MSGID_TERMINATE_SESSION:
  416.         {
  417.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  418.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  419.  
  420.             g_hrDialog = DPNERR_CONNECTIONLOST;
  421.             EndDialog( g_hDlg, 0 );
  422.             break;
  423.         }
  424.  
  425.         case DPN_MSGID_RECEIVE:
  426.         {
  427.             PDPNMSG_RECEIVE pReceiveMsg;
  428.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  429.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
  430.  
  431.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  432.             // This simple voice server doesn't expect any msgs from the clients
  433.             break;
  434.         }
  435.     }
  436.  
  437.     return S_OK;
  438. }
  439.  
  440.  
  441.  
  442.  
  443. //-----------------------------------------------------------------------------
  444. // Name: DirectPlayVoiceServerMessageHandler()
  445. // Desc: The callback for DirectPlayVoice server messages.  
  446. //-----------------------------------------------------------------------------
  447. HRESULT CALLBACK DirectPlayVoiceServerMessageHandler( LPVOID lpvUserContext, DWORD dwMessageType,
  448.                                                       LPVOID lpMessage )
  449. {
  450.     // This simple sample doesn't respond to any server messages
  451.     return S_OK;
  452. }
  453.  
  454.  
  455.  
  456.  
  457. //-----------------------------------------------------------------------------
  458. // Name: StartServer
  459. // Desc: 
  460. //-----------------------------------------------------------------------------
  461. HRESULT StartServer( HWND hDlg )
  462. {
  463.     DWORD dwSessionType;
  464.     int lCurSelection;
  465.     HRESULT hr;
  466.     PDIRECTPLAY8ADDRESS pDP8AddrLocal = NULL;
  467.  
  468.     SetDlgItemText( hDlg, IDC_STATUS, TEXT("Starting server...") );
  469.     SetCursor( LoadCursor(NULL, IDC_WAIT) );
  470.  
  471.     WCHAR wstrSessionName[MAX_PATH];
  472.     GetDlgItemText( hDlg, IDC_SESSION_NAME, g_strSessionName, MAX_PATH );
  473.     DXUtil_ConvertGenericStringToWide( wstrSessionName, g_strSessionName );
  474.  
  475.     BOOL bPortTranslated;
  476.     g_dwPort = GetDlgItemInt( hDlg, IDC_PORT, &bPortTranslated, FALSE );
  477.     if( !bPortTranslated )
  478.         g_dwPort = 0;
  479.  
  480.     // Create IDirectPlay8Server
  481.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Server, NULL, 
  482.                                        CLSCTX_INPROC_SERVER,
  483.                                        IID_IDirectPlay8Server, 
  484.                                        (LPVOID*) &g_pDPServer ) ) )
  485.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  486.  
  487.     // Init IDirectPlay8Server
  488.     if( FAILED( hr = g_pDPServer->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  489.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  490.  
  491.     hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL, 
  492.                            CLSCTX_ALL, IID_IDirectPlay8Address, 
  493.                            (LPVOID*) &pDP8AddrLocal );
  494.     if( FAILED(hr) )
  495.     {
  496.         DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  497.         goto LCleanup;
  498.     }
  499.  
  500.     hr = pDP8AddrLocal->SetSP( &CLSID_DP8SP_TCPIP );
  501.     if( FAILED(hr) )
  502.     {
  503.         DXTRACE_ERR( TEXT("SetSP"), hr );
  504.         goto LCleanup;
  505.     }
  506.  
  507.     // Add the port to pDP8AddrLocal, if the port is non-zero.
  508.     // If the port is 0, then DirectPlay will pick a port, 
  509.     // Games will typically hard code the port so the 
  510.     // user need not know it
  511.     if( g_dwPort != 0 )
  512.     {
  513.         if( FAILED( hr = pDP8AddrLocal->AddComponent( DPNA_KEY_PORT, 
  514.                                                       &g_dwPort, sizeof(g_dwPort),
  515.                                                       DPNA_DATATYPE_DWORD ) ) )
  516.             return DXTRACE_ERR( TEXT("AddComponent"), hr );
  517.     }
  518.  
  519.     DPN_APPLICATION_DESC dpnAppDesc;
  520.     ZeroMemory( &dpnAppDesc, sizeof(DPN_APPLICATION_DESC) );
  521.     dpnAppDesc.dwSize           = sizeof( DPN_APPLICATION_DESC );
  522.     dpnAppDesc.dwFlags          = DPNSESSION_CLIENT_SERVER;
  523.     dpnAppDesc.guidApplication  = g_guidApp;
  524.     dpnAppDesc.pwszSessionName  = wstrSessionName;
  525.  
  526.     hr = g_pDPServer->Host( &dpnAppDesc, &pDP8AddrLocal, 1, NULL, NULL, NULL, 0  );
  527.     if( FAILED(hr) )
  528.     {
  529.         DXTRACE_ERR( TEXT("Host"), hr );
  530.         goto LCleanup;
  531.     }
  532.  
  533.     // Get the selected compression codec
  534.     g_guidDVSessionCT = DPVCTGUID_DEFAULT;
  535.  
  536.     lCurSelection = (LONG)SendDlgItemMessage( hDlg, IDC_COMPRESSION_COMBO, CB_GETCURSEL, 0, 0 );
  537.     if( lCurSelection != CB_ERR )
  538.     {
  539.         GUID* pGuidCT = (LPGUID) SendDlgItemMessage( hDlg, IDC_COMPRESSION_COMBO, 
  540.                                                     CB_GETITEMDATA, lCurSelection, 0 );
  541.         if( pGuidCT != NULL )
  542.             g_guidDVSessionCT = (*pGuidCT);
  543.     }
  544.  
  545.     // Create a DirectPlay Voice session
  546.     g_pNetVoice = new CNetVoice( NULL, DirectPlayVoiceServerMessageHandler );
  547.  
  548.     if( IsDlgButtonChecked( hDlg, IDC_MIXING ) == BST_CHECKED )
  549.         dwSessionType = DVSESSIONTYPE_MIXING;
  550.     else
  551.         dwSessionType = DVSESSIONTYPE_FORWARDING;
  552.  
  553.     if( FAILED( g_hrDialog = g_pNetVoice->Init( hDlg, TRUE, FALSE,
  554.                                                 g_pDPServer, dwSessionType, 
  555.                                                 &g_guidDVSessionCT, NULL ) ) )
  556.     {
  557.         if( FAILED(g_hrDialog) ) 
  558.             DXTRACE_ERR( TEXT("Init"), g_hrDialog );
  559.  
  560.         EndDialog( hDlg, 0 );
  561.         return g_hrDialog;
  562.     }
  563.  
  564.     SetCursor( LoadCursor(NULL, IDC_ARROW) );
  565.     g_bServerStarted = TRUE;
  566.     SetDlgItemText( hDlg, IDC_STATUS, TEXT("Server started.") );
  567.  
  568. LCleanup:
  569.     SAFE_RELEASE( pDP8AddrLocal );
  570.     SAFE_RELEASE( pDP8AddrLocal );
  571.  
  572.     return hr;
  573. }
  574.  
  575.  
  576.  
  577.  
  578. //-----------------------------------------------------------------------------
  579. // Name: StopServer
  580. // Desc: 
  581. //-----------------------------------------------------------------------------
  582. VOID StopServer( HWND hDlg )
  583. {
  584.     if( hDlg )
  585.         SetDlgItemText( hDlg, IDC_STATUS, TEXT("Stopping server...") );
  586.     SetCursor( LoadCursor(NULL, IDC_WAIT) );
  587.  
  588.     // Disconnect from the DirectPlayVoice session, 
  589.     // and destory it if we are the host player.
  590.     SAFE_DELETE( g_pNetVoice ); 
  591.  
  592.     if( g_pDPServer )
  593.     {
  594.         g_pDPServer->Close(0);
  595.         SAFE_RELEASE( g_pDPServer );
  596.     }
  597.     g_bServerStarted = FALSE;
  598.  
  599.     SetCursor( LoadCursor(NULL, IDC_ARROW) );
  600.     if( hDlg )
  601.         SetDlgItemText( hDlg, IDC_STATUS, TEXT("Server stoped.") );
  602. }
  603.  
  604.  
  605.  
  606.     
  607. //-----------------------------------------------------------------------------
  608. // Name: DisplayPlayers
  609. // Desc: 
  610. //-----------------------------------------------------------------------------
  611. VOID DisplayPlayers( HWND hDlg )
  612. {
  613.     HRESULT hr;
  614.     DWORD dwNumPlayers = 0;
  615.     DPNID* aPlayers = NULL;
  616.  
  617.     SendMessage( GetDlgItem(hDlg, IDC_PLAYER_LIST), LB_RESETCONTENT, 0, 0 );
  618.  
  619.     if( NULL == g_pDPServer )
  620.         return;
  621.  
  622.     // Enumerate all the connected players
  623.     while( TRUE )
  624.     {
  625.         hr = g_pDPServer->EnumPlayersAndGroups( aPlayers, &dwNumPlayers, DPNENUM_PLAYERS );
  626.         if( SUCCEEDED(hr) )
  627.             break;
  628.  
  629.         if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
  630.             return;
  631.  
  632.         SAFE_DELETE_ARRAY( aPlayers );
  633.         aPlayers = new DPNID[ dwNumPlayers ];
  634.     }
  635.  
  636.     // For each player, send a "create player" message to the new player
  637.     for( DWORD i = 0; i<dwNumPlayers; i++ )
  638.     {
  639.         APP_PLAYER_INFO* pPlayerInfo = NULL;
  640.  
  641.         do
  642.         {
  643.             // Get the player context accosicated with this DPNID
  644.             // Call GetPlayerContext() until it returns something other than DPNERR_NOTREADY
  645.             // DPNERR_NOTREADY will be returned if the callback thread has not 
  646.             // yet returned from DPN_MSGID_CREATE_PLAYER, which sets the player's context
  647.             hr = g_pDPServer->GetPlayerContext( aPlayers[i], (LPVOID*) &pPlayerInfo, 0 );
  648.         } 
  649.         while( hr == DPNERR_NOTREADY ); 
  650.  
  651.         // Ignore this player if we can't get the context
  652.         if( pPlayerInfo == NULL || FAILED(hr) )
  653.             continue; 
  654.  
  655.         TCHAR strTemp[MAX_PATH];
  656.         wsprintf( strTemp, TEXT("DPNID: 0x%0.8x (%s)"), pPlayerInfo->dpnidPlayer, pPlayerInfo->strPlayerName );
  657.         int nIndex = (int)SendMessage( GetDlgItem(hDlg, IDC_PLAYER_LIST), LB_ADDSTRING, 
  658.                                        0, (LPARAM)strTemp );
  659.     }
  660.  
  661.     SAFE_DELETE_ARRAY( aPlayers );
  662. }
  663.  
  664.  
  665.  
  666.     
  667. //-----------------------------------------------------------------------------
  668. // Name: SendCreatePlayerMsg
  669. // Desc: Send the target player a creation message about the player identified
  670. //       in the APP_PLAYER_INFO struct.
  671. //-----------------------------------------------------------------------------
  672. HRESULT SendCreatePlayerMsg( APP_PLAYER_INFO* pPlayerAbout, DPNID dpnidTarget )
  673. {
  674.     GAMEMSG_CREATE_PLAYER msgCreatePlayer;
  675.     msgCreatePlayer.dwType = GAME_MSGID_CREATE_PLAYER;
  676.     msgCreatePlayer.dpnidPlayer = pPlayerAbout->dpnidPlayer;
  677.     _tcscpy( msgCreatePlayer.strPlayerName, pPlayerAbout->strPlayerName );
  678.  
  679.     DPN_BUFFER_DESC bufferDesc;
  680.     bufferDesc.dwBufferSize = sizeof(GAMEMSG_CREATE_PLAYER);
  681.     bufferDesc.pBufferData  = (BYTE*) &msgCreatePlayer;
  682.  
  683.     // DirectPlay will tell via the message handler 
  684.     // if there are any severe errors, so ignore any errors 
  685.     DPNHANDLE hAsync;
  686.     g_pDPServer->SendTo( dpnidTarget, &bufferDesc, 1,
  687.                          0, NULL, &hAsync, DPNSEND_NOLOOPBACK | DPNSEND_GUARANTEED );
  688.  
  689.     return S_OK;
  690. }
  691.  
  692.  
  693.  
  694.     
  695. //-----------------------------------------------------------------------------
  696. // Name: SendWorldStateToNewPlayer
  697. // Desc: Send the world state to the new player.  For this sample, it is just
  698. //       "create player" message for every connected player
  699. //-----------------------------------------------------------------------------
  700. HRESULT SendWorldStateToNewPlayer( DPNID dpnidNewPlayer )
  701. {
  702.     HRESULT hr;
  703.     DWORD dwNumPlayers = 0;
  704.     DPNID* aPlayers = NULL;
  705.  
  706.     // Tell this player the dpnid of itself
  707.     GAMEMSG_SET_ID msgSetID;
  708.     msgSetID.dwType      = GAME_MSGID_SET_ID;
  709.     msgSetID.dpnidPlayer = dpnidNewPlayer;
  710.  
  711.     DPN_BUFFER_DESC bufferDesc;
  712.     bufferDesc.dwBufferSize = sizeof(GAMEMSG_SET_ID);
  713.     bufferDesc.pBufferData  = (BYTE*) &msgSetID;
  714.  
  715.     // DirectPlay will tell via the message handler 
  716.     // if there are any severe errors, so ignore any errors 
  717.     DPNHANDLE hAsync;
  718.     g_pDPServer->SendTo( dpnidNewPlayer, &bufferDesc, 1,
  719.                          0, NULL, &hAsync, DPNSEND_NOLOOPBACK | DPNSEND_GUARANTEED );
  720.  
  721.     // Enumerate all the connected players
  722.     while( TRUE )
  723.     {
  724.         hr = g_pDPServer->EnumPlayersAndGroups( aPlayers, &dwNumPlayers, DPNENUM_PLAYERS );
  725.         if( SUCCEEDED(hr) )
  726.             break;
  727.  
  728.         if( FAILED(hr) && hr != DPNERR_BUFFERTOOSMALL )
  729.             return DXTRACE_ERR( TEXT("EnumPlayersAndGroups"), hr );
  730.  
  731.         SAFE_DELETE_ARRAY( aPlayers );
  732.         aPlayers = new DPNID[ dwNumPlayers ];
  733.     }
  734.  
  735.     // For each player, send a "create player" message to the new player
  736.     for( DWORD i = 0; i<dwNumPlayers; i++ )
  737.     {
  738.         APP_PLAYER_INFO* pPlayerInfo = NULL;
  739.  
  740.         // Don't send a create msg to the new player about itself.  This will 
  741.         // be already done when we sent one to DPNID_ALL_PLAYERS_GROUP
  742.         if( aPlayers[i] == dpnidNewPlayer )
  743.             continue;  
  744.  
  745.         // Get the player context accosicated with this DPNID
  746.         hr = g_pDPServer->GetPlayerContext( aPlayers[i], (LPVOID*) &pPlayerInfo, 0 );
  747.  
  748.         // Ignore this player if we can't get the context
  749.         if( pPlayerInfo == NULL || FAILED(hr) )
  750.             continue; 
  751.  
  752.         SendCreatePlayerMsg( pPlayerInfo, dpnidNewPlayer );
  753.     }
  754.  
  755.     SAFE_DELETE_ARRAY( aPlayers );
  756.  
  757.     return S_OK;
  758. }
  759.  
  760.  
  761.  
  762.  
  763. //-----------------------------------------------------------------------------
  764. // Name: SendDestroyPlayerMsgToAll
  765. // Desc: 
  766. //-----------------------------------------------------------------------------
  767. HRESULT SendDestroyPlayerMsgToAll( APP_PLAYER_INFO* pPlayerInfo )
  768. {
  769.     GAMEMSG_DESTROY_PLAYER msgDestroyPlayer;
  770.     msgDestroyPlayer.dwType = GAME_MSGID_DESTROY_PLAYER;
  771.     msgDestroyPlayer.dpnidPlayer = pPlayerInfo->dpnidPlayer;
  772.  
  773.     DPN_BUFFER_DESC bufferDesc;
  774.     bufferDesc.dwBufferSize = sizeof(GAMEMSG_CREATE_PLAYER);
  775.     bufferDesc.pBufferData  = (BYTE*) &msgDestroyPlayer;
  776.  
  777.     // DirectPlay will tell via the message handler 
  778.     // if there are any severe errors, so ignore any errors 
  779.     DPNHANDLE hAsync;
  780.     g_pDPServer->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
  781.                          0, NULL, &hAsync, DPNSEND_NOLOOPBACK | DPNSEND_GUARANTEED );
  782.  
  783.     return S_OK;
  784. }
  785.  
  786.  
  787.  
  788.  
  789.